#include "sign.h"

struct SigPair {
    unsigned char *sigma1;
    size_t sigma1_len;
    unsigned char *sigma2;
    size_t sigma2_len;
} signature;

//static int nid = OBJ_create("1.2.3.4.1.2.3.4", "sigma1", "Sigma1"); 

char *get_oqs_fromtls(const char *tlsname){
    char *oqsname;
    char *del = "_";
    char *position = strchr(tlsname, *del);
    
    if(position == NULL)
    return NULL;
    
    oqsname = position+1;
    
    return oqsname;
}

char *get_cls_fromtls(const char *tlsname) {
    char *clsname;
    char *del = "_";
    char *position = strchr(tlsname, *del);

    if (position == NULL) {
           return NULL;
    }

    size_t len = position - tlsname;

    clsname = (char *)malloc(len + 1);  

    if (clsname == NULL) {
        return NULL;
    }
    
        strncpy(clsname, tlsname, len);
    clsname[len] = '\0';

    return clsname;
}

EVP_PKEY *generate_classic_evp(const char *classic_names){
         
     EVP_PKEY *pkey = NULL;
            if(!strncmp(classic_names,"rsa", 3)){
        //        printf("1\n");
        pkey = EVP_RSA_gen(3072);
                    return pkey;
     }
           if(!strncmp(classic_names, "p256",4)){
   //                        printf("2\n");
         pkey =  EVP_EC_gen("P-256");
                       return pkey;
          }
            if(!strncmp(classic_names, "p384",4)){
   //                         printf("3\n");
         pkey =  EVP_EC_gen("P-384");
                       return pkey;
          }        
            if(!strncmp(classic_names, "p521",4)){
  //                          printf("4\n");
         pkey =  EVP_EC_gen("P-521");
                        return pkey;
          }          

    EVP_PKEY_free(pkey);
          return NULL;
}

EVP_PKEY *generate_pq_evp(OSSL_LIB_CTX *libctx, const char *oqsname){
    EVP_PKEY_CTX *ctx = NULL;
    EVP_PKEY *pqkey = NULL;
 
    if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, oqsname, OQSPROV_PROPQ))== NULL){
        printf("1\n");
        return NULL;
    }
    
    if (EVP_PKEY_keygen_init(ctx)== 0){
        printf("2\n");
        return NULL;
    }
    
    
    if (EVP_PKEY_generate(ctx, &pqkey)== 0){
        printf("3\n");
        return NULL;
    }
    
 
/*   if (!(ctx = EVP_PKEY_CTX_new_from_name(libctx, oqsname, OQSPROV_PROPQ)) ||!EVP_PKEY_keygen_init(ctx) ||!(EVP_PKEY_generate(ctx, &pqkey))) {
      fprintf(stderr, "pq generate error.\n");
      EVP_PKEY_CTX_free(ctx);
      EVP_PKEY_free(pqkey);
               return NULL;
    }*/

    EVP_PKEY_CTX_free(ctx);
        return pqkey;    
}

X509_REQ *generate_cert_req(EVP_PKEY *pkey){
    X509_REQ *cert_req = X509_REQ_new();
    X509_NAME *name = NULL;
          if(!cert_req){
          fprintf(stderr, "X509_REQ_new error.\n");
          X509_REQ_free(cert_req);
          X509_NAME_free(name);
                         return NULL;
    }
    
    if(!X509_REQ_set_pubkey(cert_req, pkey)||!(name = X509_REQ_get_subject_name(cert_req))||!X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CN", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"icatest.org", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"ICA", -1, -1, 0)||!    X509_REQ_sign(cert_req, pkey, EVP_sha256())){
          fprintf(stderr, "X509_REQ_gen error.\n");
          X509_REQ_free(cert_req);
          X509_NAME_free(name);
                          return NULL;
    }

        return cert_req;    
} 

int get_signature_data(X509_REQ *cert_req, EVP_PKEY *pkey){
           size_t ret = 0;
    X509 *cert = X509_new();
    X509_NAME *name = X509_NAME_new();
    
   if(!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0)||
  !X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"catest.org", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CN", -1, -1, 0)){
        fprintf(stderr, "X509 NAME gen error.\n");
                     goto end;
    }    
    ASN1_INTEGER_set(X509_get_serialNumber(cert),1);
    X509_gmtime_adj(X509_getm_notBefore(cert), 0);
    X509_gmtime_adj(X509_getm_notAfter(cert), 31536000L);
    
    X509_set_subject_name(cert, X509_REQ_get_subject_name(cert_req));
    X509_set_issuer_name(cert, name);
    X509_set_pubkey(cert, X509_REQ_get_pubkey(cert_req)); 
          if(!X509_sign(cert, pkey,EVP_sha256())){
        fprintf(stderr, "X509_gen error.\n");        
                     goto end;
    }
          //fprintf(stderr, "X509_gen success.\n");
 
          const ASN1_BIT_STRING *sig;
          const X509_ALGOR *sigalg = NULL;  
    X509_get0_signature(&sig, &sigalg, cert);
          size_t siglen = 0;
    siglen = sig->length;
    signature.sigma1_len = siglen;
    signature.sigma1 = (unsigned char *)malloc(siglen);
         if (signature.sigma1== NULL) {
        fprintf(stderr, "Memory allocation failed\n");
                      goto end;
    } 
    memcpy(signature.sigma1,sig->data, siglen); 
    ret = 1;
end:
    X509_free(cert);
          return ret;    
}

X509 *generate_final_cert(X509_REQ *cert_req,EVP_PKEY *pkey){
    X509 *cert = X509_new();
    ASN1_INTEGER_set(X509_get_serialNumber(cert),1);
    X509_gmtime_adj(X509_getm_notBefore(cert), 0);
    X509_gmtime_adj(X509_getm_notAfter(cert), 31536000L);
    X509_set_subject_name(cert, X509_REQ_get_subject_name(cert_req));
    X509_set_pubkey(cert, X509_REQ_get_pubkey(cert_req)); 
    X509_NAME *name = X509_NAME_new(); 
    
       if(!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0)||
  !X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"catest.org", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CN", -1, -1, 0)){
        fprintf(stderr, "X509 NAME gen error.\n");
                     goto end;
    }
    X509_set_issuer_name(cert, name);
          
    X509_EXTENSION *sigma1 = NULL;
    ASN1_OCTET_STRING *data = ASN1_OCTET_STRING_new();
    
   // X509V3_EXT_add_alias(nid, NID_sigma_ext);
          if(!ASN1_OCTET_STRING_set(data, signature.sigma1, signature.sigma1_len)){
        fprintf(stderr, "Failed to create ASN1_OCTET_STRING for extension value.\n");
                      goto end;
    }
    
           if(!(sigma1 = X509_EXTENSION_create_by_NID(&sigma1, NID_subject_alt_name  ,1 ,data))){
        fprintf(stderr, "X509_EXTENSION create failed. \n");
                      goto end;
    }

    X509_add_ext(cert, sigma1, 0);
          if(cert == NULL){
      }
          if(!X509_sign(cert, pkey,EVP_sha256())){
        fprintf(stderr, "X509 final gen error.\n");        
                     goto end;
    }
    
//    fprintf(stderr,"Sigma2 gen success.\n"); 
    
         return cert;
end:
        X509_free(cert);
        X509_EXTENSION_free(sigma1);
   ASN1_OCTET_STRING_free(data);
        return NULL;
}

int write_privkey_to_file(const char *filename, EVP_PKEY *pkey) {  
    FILE *file = fopen(filename, "wb");  
    if (file == NULL) {  
        fprintf(stderr, "Failed to open file for writing");  
    }  
  
    if (!PEM_write_PrivateKey(file, pkey, NULL, NULL, 0, NULL, NULL)) {  
        fclose(file);  
        fprintf(stderr,"Failed to write EVP_PKEY to file");  
    }  
  
    fclose(file);  
          return 1;  
}

int write_x509_to_file(const char *filename, X509 *cert) {  
    FILE *file = fopen(filename, "wb");  
         if (file == NULL) {  
    fprintf(stderr, "Failed to open file for writing\n"); 
       return 0; 
    }    
    if (!PEM_write_X509(file, cert)) {  
        fprintf(stderr, "Failed to write X509 to file\n"); 
        fclose(file);  
               return 0; 
    }  
  
  fclose(file);  
    return 1;  
}  

int create_hybrid_cert(OSSL_LIB_CTX *libctx,const char *signames,char *key_addr ,char certpath[300], char classkeypath[300], char oqskeypath[300]){

    EVP_PKEY *class_pkey = NULL;
    EVP_PKEY *pq_pkey = NULL;
    int ret = 1;
    char *oqsname, *clasname;

    signature.sigma1 = NULL;
    signature.sigma2 = NULL;
    signature.sigma1_len = 0;
    signature.sigma2_len = 0;

    oqsname = get_oqs_fromtls((char *)signames);
    clasname = get_cls_fromtls((char *)signames);
    printf("%s\n", oqsname);
    printf("%s\n", clasname);
    //
       
    class_pkey  = generate_classic_evp(clasname);
    pq_pkey = generate_pq_evp(libctx, oqsname);
   
    X509_REQ *cert_req = X509_REQ_new();
    //clock_t start_time = clock();  
    //unsigned char* sigma1;
/*    cert_req = generate_cert_req(class_pkey); 
  //      cert_req = generate_cert_req(pq_pkey);
          if(get_signature_data(cert_req, class_pkey)){
//       fprintf(stderr,"Sigma1 gen success.\n");
    }else{
       fprintf(stderr,"Sigma1 gen failed.\n");  
                   goto end;  
    }*/
    cert_req = generate_cert_req(pq_pkey);
    X509 *final_cert = X509_new();
    if(get_signature_data(cert_req, class_pkey)){
           //fprintf(stderr,"Sigma1 gen success.\n");
    }else{
       //fprintf(stderr,"Sigma1 gen failed.\n");  
                   goto end;  
    }    

    final_cert = generate_final_cert(cert_req, pq_pkey);
          fprintf(stderr,cGREEN "Hybrid cert generate success." cNORM "\n");
          if(final_cert == NULL){
          fprintf(stderr,cRED "Hybrid cert generate failed." cNORM "\n");
       X509_free(final_cert);
                  goto end;
    }
    //clock_t end_time = clock();     
    //clock_t total_time = end_time - start_time;  
    //      double microseconds = ((double)total_time) * (1000000.0 / CLOCKS_PER_SEC);          
    //fprintf(stderr, "程序运行时间: %.0f 微秒\n", microseconds);
    
          const char *sep = "/";
    
    sprintf(classkeypath, "%s%s%s%s", key_addr, sep, signames, "_cls.key");
    sprintf(oqskeypath, "%s%s%s%s", key_addr, sep, signames, "_oqs.key");
    sprintf(certpath, "%s%s%s%s", key_addr, sep, signames, ".crt");  
    

    write_privkey_to_file(classkeypath, class_pkey);
    write_privkey_to_file(oqskeypath, pq_pkey);
    write_x509_to_file(certpath, final_cert);   

    X509_free(final_cert);   
    ret = 0;
end:
    X509_REQ_free(cert_req);
    EVP_PKEY_free(class_pkey);
    EVP_PKEY_free(pq_pkey);
    return ret;
}

